/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.dnd.oleenumformatetc;


private import dwt.dwt;

private import dwt.internal.ole.win32.com;
private import dwt.internal.ole.win32.OBJIDL;
private import dwt.internal.ole.win32.extras;


class _IEnumFORMATETCImpl : IEnumFORMATETC {

	private import std.c.windows.windows;


	OleEnumFORMATETC	parent;
	this(OleEnumFORMATETC	p) { parent = p; }
extern (Windows):
	// interface of IUnknown
	HRESULT QueryInterface(REFIID riid, void ** ppvObject) { return parent.QueryInterface(riid, ppvObject); }
	ULONG AddRef()	{ return parent.AddRef(); }
	ULONG Release()	{ return parent.Release(); }

	// interface of IEnumFORMATETC
	HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
		return parent.Next(celt, rgelt, pceltFetched);
	}
	HRESULT Skip(ULONG celt) { return parent.Skip(celt); }
	HRESULT Reset() { return parent.Reset(); }
	HRESULT Clone(IEnumFORMATETC * ppenum) { return COM.E_NOTIMPL;}
}


class OleEnumFORMATETC {

	private import std.c.windows.windows;

	private _IEnumFORMATETCImpl iEnumFORMATETC;

	private int refCount;
	private int index;

	private FORMATETC*[] formats;

this() {
	createCOMInterfaces();
}

ULONG AddRef() {
	refCount++;
	return refCount;
}

protected void createCOMInterfaces() {
	// register each of the interfaces that this object implements
	iEnumFORMATETC = new _IEnumFORMATETCImpl(this);
}

protected void disposeCOMInterfaces() {
	iEnumFORMATETC = null;
}

IEnumFORMATETC getAddress() {
	return iEnumFORMATETC;
}

private FORMATETC*[] getNextItems(int numItems){

	if (formats is null || numItems < 1) return null;

	int endIndex = index + numItems - 1;
	if (endIndex > (formats.length - 1)) endIndex = formats.length - 1;
	if (index > endIndex) return null;

	FORMATETC*[] items =  new FORMATETC*[endIndex - index + 1];
	for (int i = 0; i < items.length; i++){
		items[i] = formats[index];
		index++;
	}

	return items;
}


package HRESULT Next(ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) {
	/* Retrieves the next celt items in the enumeration sequence.
	   If there are fewer than the requested number of elements left in the sequence,
	   it retrieves the remaining elements.
	   The number of elements actually retrieved is returned through pceltFetched
	   (unless the caller passed in NULL for that parameter).
	*/

	if (rgelt is null)	return COM.E_INVALIDARG;
	if (pceltFetched is null && celt != 1) return COM.E_INVALIDARG;

	FORMATETC*[] nextItems = getNextItems(celt);
	if (nextItems !is null) {
		for (int i = 0; i < nextItems.length; i++) {
			COM.MoveMemory(rgelt + i*FORMATETC.sizeof, nextItems[i], FORMATETC.sizeof);
		}

		if (pceltFetched !is null) {
			int[] intArr;
			intArr ~= nextItems.length;
			COM.MoveMemory(pceltFetched, intArr.ptr, 4);
		}

		if (nextItems.length == celt) return COM.S_OK;

	} else {
		if (pceltFetched !is null) {
			int[] intArr;
			intArr ~= 0;
			COM.MoveMemory(pceltFetched, intArr.ptr, 4);
		}
		COM.MoveMemory(rgelt, new FORMATETC(), FORMATETC.sizeof);

	}
	return COM.S_FALSE;
}

// NOTE: change from private to package
HRESULT QueryInterface(REFIID riid, void ** ppvObject) {

	if (riid is null || ppvObject is null) return COM.E_NOINTERFACE;


	if (COM.IsEqualGUID(riid, &COM.IIDIUnknown) || COM.IsEqualGUID(riid, &COM.IIDIEnumFORMATETC)) {
		*ppvObject = cast(void*)(cast(IEnumFORMATETC)iEnumFORMATETC);
		AddRef();
		return COM.S_OK;
	}
	*ppvObject = null;
	return COM.E_NOINTERFACE;
}
ULONG Release() {
	refCount--;

	if (refCount == 0) {
		disposeCOMInterfaces();
		COM.CoFreeUnusedLibraries();
	}

	return refCount;
}


package HRESULT Reset() {
	//Resets the enumeration sequence to the beginning.
	index = 0;
	return COM.S_OK;
}
void setFormats(FORMATETC*[] newFormats) {
	formats = newFormats;
	index = 0;
}

package HRESULT Skip(ULONG celt) {
	//Skips over the next specified number of elements in the enumeration sequence.
	if (celt < 1 ) return COM.E_INVALIDARG;

	index += celt;
	if (index > (formats.length - 1)){
		index = formats.length - 1;
		return COM.S_FALSE;
	}
	return COM.S_OK;
}
}
